package com.wootion.cims.module.landing.login.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.wootion.cims.dao.SysConfigurationDao;
import com.wootion.cims.dao.SysLogDao;
import com.wootion.cims.dao.UserDao;
import com.wootion.cims.general.enumtype.Security;
import com.wootion.cims.general.utils.GetIp;
import com.wootion.cims.general.utils.RedisUtil;
import com.wootion.cims.module.landing.login.service.LoginService;
import com.wootion.cims.module.systemsetting.log.model.SysLog;
import com.wootion.cims.module.systemsetting.system.model.SysConfiguration;
import com.wootion.cims.module.systemsetting.user.model.User;
import com.wootion.cims.response.ResponseResult;
import com.wootion.cims.response.ResultCode;
import com.wootion.cims.shiro.config.JWTFilter;
import com.wootion.cims.shiro.utils.JwtUtil;
import com.wootion.cims.shiro.utils.ShiroUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created with IntelliJ IDEA.
 *
 * @Author: jack
 * @Date: 2022/12/02/13:35
 * @Description:
 */
@Slf4j
@Service
public class LoginServiceImpl implements LoginService {

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private UserDao userDao;

    @Resource
    private SysLogDao sysLogDao;

    @Resource
    private SysConfigurationDao sysConfigurationDao;


    /**
     * 登陆
     *
     * @param user
     * @return
     * @throws UnsupportedEncodingException
     */
    @Override
    public ResponseResult login(User user, HttpServletRequest request) throws UnsupportedEncodingException {
        //判断redis中是否已经被锁定了10分钟
        if (!Optional.ofNullable(redisUtil.getCacheObject("login:" + user.getUsername() + "stuts")).isPresent()) {//仅在登录账户状态正常时进行登录
            //如果没有锁定10分钟，则如下：
            LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
            lambdaQueryWrapper.eq(StringUtils.hasText(user.getUsername()), User::getUsername, user.getUsername());
            lambdaQueryWrapper.eq(User::getEnabled, true);
            User user1 = userDao.selectOne(lambdaQueryWrapper);
            if (Objects.isNull(user1)) {
                //没有在数据库中查询到，返回账号密码错误
                return ResponseResult.builder().code(ResultCode.AUTH_FAIL.getCode()).message(ResultCode.AUTH_FAIL.getMessage()).build();
            }
            String id = user1.getId();
            //记录登陆日志
            SysLog sysLog = new SysLog();
            sysLog.setCreateTime(new Date());//登陆时间
            sysLog.setIp(GetIp.getIpAddr(request));//登陆ip
            sysLog.setOperator(user1.getUsername());//操作人
            sysLog.setValue("系统日志");
            sysLog.setType("登录系统");
            sysLog.setIncident("个人权限");//事件
            sysLog.setContent("登陆系统");
            //密码正确
            //密码加密规则: 明文密码 + （当前用户名 + 固定的特殊盐）
            //如果可以根据username查询到，接下里再判断密码
            if (ShiroUtils.verifyPasswordSalt(user.getPassword(), user1.getUsername() + ShiroUtils.SALT, user1.getPassword())) {
                if (user1.getEnabled().equals(false)) {
                    //密码验证通过之后，如果账号已经被禁用，但是依然返回账号密码错误
                    return ResponseResult.builder().code(ResultCode.AUTH_FAIL.getCode()).message("该账号已经被禁用！").build();
                }
                if(!"正常".equals(user1.getAccount())){
                    return ResponseResult.builder().code(ResultCode.AUTH_FAIL.getCode()).message("你的账号已停用,请联系管理员!").build();
                }
                log.info("========开始记录登陆日志==========");
                redisUtil.deleteObject("login:" + user.getUsername() + "loginNumber");
                //这里根据用户的id,生成一个token,独一无二
                String token = JwtUtil.createToken(id);
                Map<String, String> map = new HashMap<>();
                map.put(JWTFilter.TOKEN_NAME_TWO, token);
                redisUtil.setCacheObject("user:" + token, user1, 24, TimeUnit.HOURS);
                sysLog.setResult("成功");
                sysLogDao.insert(sysLog);
                return ResponseResult.builder().data(map).build();
            } else {
                sysLog.setResult("失败");
                sysLogDao.insert(sysLog);
                Integer cacheObject = (Integer) redisUtil.getCacheObject("login:" + user.getUsername() + "loginNumber");
                if (Optional.ofNullable(cacheObject).isPresent() && cacheObject >= 9) {//失败次数超过10次
                    redisUtil.setCacheObject("login:" + user.getUsername() + "stuts", "1", 30, TimeUnit.MINUTES);
                    redisUtil.deleteObject("login:" + user.getUsername() + "loginNumber");
                    return ResponseResult.builder().code(ResultCode.AUTH_FAIL.getCode()).message("账号已被锁定，请稍后再尝试!").build();
                } else {//失败次数小于5次
                    if (Optional.ofNullable(cacheObject).isPresent()) {
                        Long expire = redisUtil.getExpire("login:" + user.getUsername() + "loginNumber");
                        redisUtil.setCacheObject("login:" + user.getUsername() + "loginNumber", cacheObject + 1,expire.intValue(),TimeUnit.SECONDS);
                        return ResponseResult.builder().code(ResultCode.AUTH_FAIL.getCode()).message(ResultCode.AUTH_FAIL.getMessage() + ",你还剩" + (9 - cacheObject) + "次机会").build();
                    } else {//第一次失败
                        redisUtil.setCacheObject("login:" + user.getUsername() + "loginNumber", 1,24,TimeUnit.HOURS);
                        return ResponseResult.builder().code(ResultCode.AUTH_FAIL.getCode()).message(ResultCode.AUTH_FAIL.getMessage() + ",你还剩" + (10 - 1) + "次机会").build();
                    }
                }
            }
        }
        //如果还在锁定中的10分钟之内，则如下：
        return ResponseResult.builder().code(ResultCode.AUTH_FAIL.getCode()).message("账号已被锁定，请稍后再尝试！").build();
    }

    /**
     * 检查密码是否需要修改
     *
     * @return
     */
    @Override
    public ResponseResult checked() {
        User user = (User) ShiroUtils.getSubject().getPrincipal();
        User user1 = userDao.selectById(user);
        LambdaQueryWrapper<SysConfiguration> queryWrapper10 = new LambdaQueryWrapper<>();
        queryWrapper10.eq(SysConfiguration::getCode, Security.PASSWORD_TIPS_TYPE.getCode());
        SysConfiguration sysConfiguration = sysConfigurationDao.selectOne(queryWrapper10);
        Map<String, String> msg = new HashMap<>();
        String value1 = sysConfiguration.getValue();

        if (!user1.getUsername().equals("admin")) {
            Date modifyPasswordTime = user1.getModifyPasswordTime();
            //首次登录，必须改密码，如果不改，不让进入系统，而且每次登录都提示
            if(Objects.isNull(modifyPasswordTime)){
                msg.put("unmodified","初次登录,还未修改密码，请修改初始密码后再登录！");
                return ResponseResult.builder().data(msg).build();
            }else{
                //不是首次登录，已经修改过密码了
                if (value1.equals("onlyone")) {//只弹出一次
                    //首先判断有没有弹过窗
                    Object cacheObject = redisUtil.getCacheObject("popover:" + user1.getId());
                    if(Objects.isNull(cacheObject)){//没有弹过窗
                        //判断时间是否超过3个月时间了
                        LambdaQueryWrapper<SysConfiguration> queryWrapper = new LambdaQueryWrapper<>();
                        queryWrapper.eq(SysConfiguration::getCode, Security.PASSWORD_TIPS_CYCLE.getCode());
                        SysConfiguration sysConfiguration2 = sysConfigurationDao.selectOne(queryWrapper);
                        String value = sysConfiguration2.getValue();
                        int integer = Integer.parseInt(value);
                        long expireTime = 1000L * 24 * 60 * 60 * 30 * integer;
                        long time = modifyPasswordTime.getTime();//上次修改密码的时间毫秒值
                        long time1 = new Date().getTime();//当前登录的时间
                        long difference = time1 - time;
                        if (difference > expireTime) {//超过时间周期了，提示
                            msg.put("tips", "长时间没有修改密码,请修改密码");
                            redisUtil.setCacheObject("popover:" + user1.getId(), "true");
                            return ResponseResult.builder().data(msg).build();
                        }else {
                            //还没有超过3个月的时间周期
                            return ResponseResult.builder().build();
                        }
                    }else{
                       //已经弹过窗了，那么也不会弹窗
                        return ResponseResult.builder().build();
                    }
                } else {// everytime 每次都弹
                    //看看是否超过3个月的时间周期
                    LambdaQueryWrapper<SysConfiguration> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(SysConfiguration::getCode, Security.PASSWORD_TIPS_CYCLE.getCode());
                    SysConfiguration sysConfiguration2 = sysConfigurationDao.selectOne(queryWrapper);
                    String value = sysConfiguration2.getValue();
                    int integer = Integer.parseInt(value);
                    long expireTime = 1000L * 24 * 60 * 60 * 30 * integer;
                    long time = modifyPasswordTime.getTime();//上次修改密码的时间毫秒值
                    long time1 = new Date().getTime();//当前登录的时间
                    long difference = time1 - time;
                    if (difference > expireTime) {//超过时间周期了，提示
                        msg.put("tips", "长时间没有修改密码,请修改密码");
                        return ResponseResult.builder().data(msg).build();
                    }else {
                        //还没有超过3个月的时间周期
                        return ResponseResult.builder().build();
                    }
                }
            }
        }else{
            //如果是admin登录，则不受限制
            return ResponseResult.builder().build();
        }
    }
}
